package core;

import static org.junit.Assert.*;

import java.util.Calendar;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import core.exception.IllegalAddException;

public class ItemManagerTest {

	private Calendar now;
	private Calendar expires;
	private Calendar may4th2013;
	private Calendar longAgo;
	
	private ItemManager _im;
	private RootStorageUnit _rsu;
	private Container _su1;
	private Container _su2;
	private Container _5Items;
	private Container _noItems;
	
	private Product _p1;
	private Product _p2;
	private Product _p3;
	private Product _pNotUsed;
	private Product _pExpired;
	private Product _pOneItem;
	
	private Item _i1p1;
	private Item _i2p1;
	private Item _i3p1;
	private Item _i1p2;
	private Item _i2p2;
	private Item _i3p2;
	private Item _i1p3;
	private Item _i2p3;
	private Item _i3p3;
	private Item _i4p3;
	private Item _i5p3;
	
	private Item _ipOneItem;
	
	private Item _i1su1;
	private Item _i2su1;
	private Item _i1su2;
	private Item _i2su2;
	
	private Item _iExpired1;
	private Item _iExpired2;
	private Item _iExpired3;
	
	private Item _i51;
	private Item _i52;
	private Item _i53;
	private Item _i54;
	private Item _i55;
	
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}

	@Before
	public void setUp() throws Exception {
		ItemManager.setInstance(null);
		_im = ItemManager.instance();
		_rsu = RootStorageUnit.instance();
		_su1 = _rsu.addContainer("su1");
		_su2 = _rsu.addContainer("su2");
		_5Items = _rsu.addContainer("5items");
		_noItems = _rsu.addContainer("noItems");
		
		longAgo = Calendar.getInstance();
		longAgo.set(2000, 4, 4);
		may4th2013 = Calendar.getInstance();
		may4th2013.set(2013, 4, 4);
		
		//make a bunch of products
		_p1 = new Product(new Barcode("111111111111", BarcodeType.PRODUCT), "myProduct1", new Amount(1, UnitOfMeasurement.count),30,0);
		_p2 = new Product(new Barcode("111111111112", BarcodeType.PRODUCT), "myProduct2", new Amount(2, UnitOfMeasurement.count),30,0);
		_p3 = new Product(new Barcode("111111111113", BarcodeType.PRODUCT), "myProduct3", new Amount(3, UnitOfMeasurement.count),30,0);
		_pNotUsed = new Product(new Barcode("111111111114", BarcodeType.PRODUCT), "myProduct4", new Amount(1, UnitOfMeasurement.count),30,0);
		_pExpired = new Product(new Barcode("111111111115", BarcodeType.PRODUCT), "expiredProduct", new Amount(2, UnitOfMeasurement.count), 1, 0);
		_pOneItem = new Product(new Barcode("111111111116", BarcodeType.PRODUCT), "onlyOneProduct", new Amount(3, UnitOfMeasurement.count), 1, 0);
		ProductManager.instance().addProduct(_p1);
		ProductManager.instance().addProduct(_p2);
		ProductManager.instance().addProduct(_p3);
		ProductManager.instance().addProduct(_pNotUsed);
		ProductManager.instance().addProduct(_pExpired);
		ProductManager.instance().addProduct(_pOneItem);
		
		//make a bunch of items
		_i1p1 = _im.createItem(_p1, _rsu);
		_i2p1 = _im.createItem(_p1, _rsu);
		
		_i1p2 = _im.createItem(_p2, _rsu);
		_i2p2 = _im.createItem(_p2, _rsu);
		
		_i1p3 = _im.createItem(_p3, _su1);
		_i2p3 = _im.createItem(_p3, _su1);
		
		_i1su1 = _im.createItem(_p1, _su1);
		_i2su1 = _im.createItem(_p1, _su1);
		_i1su2 = _im.createItem(_p1, _su2);
		_i2su2 = _im.createItem(_p1, _su2);
		
		_ipOneItem = _im.createItem(_pOneItem, _su2);
	}

	@After
	public void tearDown() throws Exception {
		ItemManager.setInstance(null);
		RootStorageUnit.setInstance(null);
		ProductManager.setInstance(null);
	}

	@Test
	public void testNumItems() throws Exception {
		assertEquals(_im.numItemsInProductAndContainer(_rsu, _p1), 2);
		assertEquals(_im.numItemsInProductAndContainer(_rsu, _p2), 2);
		_i3p1 = _im.createItem(_p1, _rsu);
		assertEquals(_im.numItemsInProductAndContainer(_rsu, _p1), 3);
		_i3p2 = _im.createItem(_p2, _rsu);
		assertEquals(_im.numItemsInProductAndContainer(_rsu, _p2), 3);
	}
	
	@Test
	public void testCreateItem() throws Exception{
		_i3p1 = _im.createItem(_p1, _rsu);
		Barcode barcode = _i3p1.getBarcode();
		//barcode to item
		assertEquals(_im.lookupByBarcode(barcode), _i3p1);
		assertEquals(_im.lookupByBarcode(_i2p1.getBarcode()), _i2p1);
		
		//product to items
		
		//container to items
		Iterator<Item> iter = _im.itemsInContainer(_rsu);
		iter.next();
		assertTrue("There should be 5 items in the Root storage unit", iter.hasNext());
		iter.next();
		assertTrue("There should be 5 items in the Root storage unit", iter.hasNext());
		iter.next();
		assertTrue("There should be 5 items in the Root storage unit", iter.hasNext());
		iter.next();
		assertTrue("There should be 5 items in the Root storage unit", iter.hasNext());
		iter.next();
		assertFalse("There shouldn't be 6 items in the Root storage unit", iter.hasNext());
		
		//container and product to items
		iter = _im.itemsInProductAndContainer(_rsu, _p1);
		iter.next();
		assertTrue("There should be 3 items in _p1 in _rsu", iter.hasNext());
		iter.next();
		assertTrue("There should be 3 items in _p1 in _rsu", iter.hasNext());
		iter.next();
		assertFalse("There shouldn't be 4 items in _p1 in _rsu", iter.hasNext());
	}
	
	@Test
	public void testRemoveItem() throws Exception{
		_i3p1 = _im.createItem(_p1, _rsu);
		Barcode barcode = _i3p1.getBarcode();
		
		_im.removeItem(_i3p1);
		Iterator<Item> iter = _im.getRemovedItems(longAgo);
		Item tempItem = iter.next();
		assertEquals(_i3p1, tempItem);
		
		//remove an item that's already been removed
		_im.removeItem(_i3p1);
		
		_i3p3 = _im.createItem(_p3, _rsu);
		_i4p3 = _im.createItem(_p3, _rsu);
		_i5p3 = _im.createItem(_p3, _rsu);
		
		_im.removeItem(_i3p3);
		_im.removeItem(_i4p3);
		_im.removeItem(_i5p3);
		iter = _im.getRemovedItems(longAgo);
		iter.next();
		assertTrue("There should be 4 items in removedItems", iter.hasNext());
		iter.next();
		assertTrue("There should be 4 items in removedItems", iter.hasNext());
		iter.next();
		assertTrue("There should be 4 items in removedItems", iter.hasNext());
		iter.next();
		assertFalse("There shouldn't be 5 items in removedItems", iter.hasNext());
	}
	
	@Test
	public void testMoveItem() throws IllegalAddException{
		assertEquals(_i1su1.getContainer(), _su1);
		_im.moveItem(_su2,_i1su1);
		assertEquals(_i1su1.getContainer(), _su2);
		
		assertEquals(_i2su1.getContainer(), _su1);
		_im.moveItem(_su2,_i2su1);
		assertEquals(_i2su1.getContainer(), _su2);
		
		//move it back
		assertEquals(_i1su1.getContainer(), _su2);
		_im.moveItem( _su1,_i1su1);
		assertEquals(_i1su1.getContainer(), _su1);
		
		
		// commented out because not currently able to move items to root storage unit.
		
		//move one from a place it's not located
		//_im.moveItem( _rsu,_i2su1);
		//assertEquals(_i2su1.getContainer(), _rsu);
	}
	
	@Test
	public void testGetRemovedItems() throws Exception{
		//one item
		_im.removeItem(_i1p1);
		
		Iterator<Item> iter = _im.getRemovedItems(longAgo);
		assertTrue("There should be 1 item in removedItems", iter.hasNext());
		Item tempItem = iter.next();
		assertEquals(tempItem, _i1p1);
		assertFalse("There shouldn't be 2 items in removedItems", iter.hasNext());
		
		//zero items
		iter = _im.getRemovedItems(may4th2013);
		assertFalse("There shouldn't be any items removed after May 4th, 2013", iter.hasNext());
	}
	
	@Test
	public void testGetExpiredItems() throws Exception{
		//add some expired items. _pExpired has a shelf life of 1 month
		_iExpired1 = _im.createItem(_pExpired, _rsu);
		_iExpired1.changeEntryDate("03/11/1988");
		_iExpired2 = _im.createItem(_pExpired, _su1);
		_iExpired2.changeEntryDate("03/11/1987");
		_iExpired3 = _im.createItem(_pExpired, _su2);
		_iExpired3.changeEntryDate("03/11/1986");
		
		/*
		Iterator<Item> iter = _im.getExpiredItems(longAgo);
		iter.next();
		assertTrue("There should be 3 expired items", iter.hasNext());
		iter.next();
		assertTrue("There should be 3 expired items", iter.hasNext());
		iter.next();
		assertFalse("There shouldn't be 4 expired items", iter.hasNext());
		
		//remove one of the expired items
		_im.removeItem(_iExpired1.getBarcode());
		iter = _im.getExpiredItems(longAgo);
		iter.next();
		assertTrue("There should be 2 expired items", iter.hasNext());
		iter.next();
		assertFalse("There shouldn't be 3 expired items", iter.hasNext());
		*/
	}
	
	@Test
	public void testOneRemoveReferencesToContainer(){
		//removing one container
		Collection<Container> containers = new HashSet<Container>();
		containers.add(_su1);
		_im.removeReferencesToContainer(containers);
		
		Iterator<Item> iter = _im.itemsInContainer(_su1);
		assertEquals(iter, null);
		
		iter = _im.itemsInContainer(null);
		assertEquals(iter, null);
	}
	
	@Test
	public void testManyRemoveReferencesToContainer(){
		//removing all containers
		Collection<Container> containers = new HashSet<Container>();
		containers.add(_su1);
		containers.add(_su2);
		_im.removeReferencesToContainer(containers);
		Iterator<Item> iter = _im.itemsInContainer(_su1);
		assertEquals(iter, null);
		
		iter = _im.itemsInContainer(_su2);
		assertEquals(iter, null);

	}
	
	@Test
	public void testChangeContainerForProduct(){
		//product that has one item
		_im.changeContainerForProduct(_su2, _pOneItem, _su1);
		Iterator<Item> iter = _im.itemsInProductAndContainer(_su1, _pOneItem);
		iter.next();
		assertFalse("There should only be one item for that product and container", iter.hasNext());
		
//		iter = _im.itemsInProductAndContainer(_su2, _pOneItem);
//		assertEquals(iter, null);
		
		//product that has many items
//		_im.changeContainerForProduct(_i1p1.getContainer(), _p1, _su2);
//		iter = _im.itemsInProductAndContainer(_rsu, _p1);
//		assertEquals(iter, null);
		
		//product that has zero items
//		_im.changeContainerForProduct(_su1, _pNotUsed, _su2);
//		iter = _im.itemsInProductAndContainer(_su2, _pNotUsed);
//		assertEquals(iter, null);
//		iter = _im.itemsInProductAndContainer(_su1, _pNotUsed);
//		assertEquals(iter, null);
	}
	
	@Test
	public void testItemsInProductAndContainer(){
		//product that has one item
		_im.changeContainerForProduct(_su2, _pOneItem, _su1);
		Iterator<Item> iter = _im.itemsInProductAndContainer(_su1, _pOneItem);
		iter.next();
		assertFalse("There should only be one item for that product and container", iter.hasNext());
		
//		iter = _im.itemsInProductAndContainer(_su2, _pOneItem);
//		assertEquals(iter, null);
		
		//product that has many items
//		_im.changeContainerForProduct(_i1p1.getContainer(), _p1, _su2);
//		iter = _im.itemsInProductAndContainer(_rsu, _p1);
//		assertEquals(iter, null);
		
		//product that has zero items
//		_im.changeContainerForProduct(_su1, _pNotUsed, _su2);
//		iter = _im.itemsInProductAndContainer(_su2, _pNotUsed);
//		assertEquals(iter, null);
//		iter = _im.itemsInProductAndContainer(_su1, _pNotUsed);
//		assertEquals(iter, null);
	}
	
	@Test
	public void testItemsInContainer() throws Exception{
		_i51 = _im.createItem(_p1, _5Items);
		//one item
		Iterator<Item> iter = _im.itemsInContainer(_5Items);
		iter.next();
		assertFalse("There should only be one", iter.hasNext());
		
		_i52 = _im.createItem(_p1, _5Items);
		_i53 = _im.createItem(_p1, _5Items);
		_i54 = _im.createItem(_p1, _5Items);
		_i55 = _im.createItem(_p1, _5Items);
		//multiple items
		iter = _im.itemsInContainer(_5Items);
		iter.next();
		assertTrue("There should be 5 items", iter.hasNext());
		iter.next();
		assertTrue("There should be 5 items", iter.hasNext());
		iter.next();
		assertTrue("There should be 5 items", iter.hasNext());
		iter.next();
		assertTrue("There should be 5 items", iter.hasNext());
		iter.next();
		assertFalse("There shouldn't be 6 items", iter.hasNext());
		
		//zero items
		iter = _im.itemsInContainer(_noItems);
		assertEquals(iter, null);
	}

}
